home *** CD-ROM | disk | FTP | other *** search
/ Super PC 31 / Super PC 31 (Shareware).iso / spc / inter / winpm223 / forms / mailmrge / mailmrge.c next >
Encoding:
C/C++ Source or Header  |  1995-11-21  |  15.7 KB  |  544 lines

  1. //
  2. //  MAILMRGE.C
  3. //  Pegasus Mail for Windows extension providing a simple
  4. //  "mail merge" capability.
  5. //
  6. //  Copyright (c) 1994-95, David Harris, All Rights Reserved.
  7. //
  8. //  The author grants explicit permission for this source code to be
  9. //  used or modified as required, subject only to the conditions that
  10. //  the copyright notices above are preserved and that by using this
  11. //  code you agree that the code is provided without warranty of any
  12. //  kind, either explicit or implied, and you use it at your own
  13. //  risk.
  14. //
  15.  
  16. //  If you are using Borland C v4.0 or earlier, uncomment the next
  17. //  line to prevent the Win32 porting layer code from being used.
  18. //  #define NOPORTLAYER
  19.  
  20. #define STRICT
  21. #include <windows.h>
  22.  
  23. #ifndef NOPORTLAYER
  24. #include <windowsx.h>
  25. #endif
  26.  
  27. #include <bwcc.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <io.h>               //  for "access()"
  31. #include "..\wpmforms.h"
  32. #include "mailmrge.h"
  33.  
  34. int atoi (const char *buf);
  35.  
  36. #ifndef MAXFPATH
  37. #define MAXFPATH 128
  38. #endif
  39.  
  40. char helpname [MAXFPATH];
  41. char help_used = 0;
  42.  
  43. #define MAXFIELDS 32    //  Maximum number of fields per record
  44.  
  45. char szFormDlgClassName [] = "bordlg_mrg";
  46. char do_logging = 1;
  47. HFONT hLogFont;
  48. HWND last_focus;
  49.  
  50. int register_form_classes (void);
  51.  
  52. HINSTANCE  hLibInstance;            // set in LibMain, used throughout the DLL
  53.  
  54.  
  55. #pragma warn -par
  56.  
  57.  
  58. BOOL FAR PASCAL _export generic_proc (HWND hDialog, UINT wMsg, 
  59.    WPARAM wParam, LPARAM lParam)
  60.    {
  61.    //  General purpose dialog procedure - simply exits as soon
  62.    //  as any button is pressed returning the ID of the button.
  63.  
  64.    BOOL fProcessed = TRUE;
  65.  
  66.    switch (wMsg)
  67.       {
  68.       case WM_INITDIALOG :
  69. //         centre_window (hDialog);
  70.          break;
  71.  
  72.       case WM_COMMAND :
  73. #ifdef NOPORTLAYER
  74.          if (HIWORD (lParam) != BN_CLICKED) break;
  75.          EndDialog (hDialog, wParam);
  76. #else
  77.          if (GET_WM_COMMAND_CMD(wParam, lParam) != BN_CLICKED) break;
  78.          EndDialog (hDialog, GET_WM_COMMAND_ID(wParam, lParam));
  79. #endif
  80.          break;
  81.  
  82.       default:
  83.          fProcessed = FALSE;
  84.          break;
  85.       }
  86.  
  87.    return fProcessed;
  88.    }
  89.  
  90.  
  91. static void report_error (char *dlg_name)
  92.    {
  93.    FARPROC iproc;
  94.  
  95.    iproc = MakeProcInstance ((FARPROC) generic_proc, hLibInstance);
  96.    DialogBox (hLibInstance, dlg_name, NULL, (DLGPROC) iproc);
  97.    FreeProcInstance (iproc);
  98.    }
  99.  
  100.  
  101. WORD FAR PASCAL _export FORMINIT (WORD version, int variant, HWND hParent,
  102.    char *data, HWND *hDialog, char *callback_name)
  103.    {
  104.    //  First, check to see if the version of the form manager is
  105.    //  one we can work with. This check is pretty arbitrary - you
  106.    //  should probably assume that you may not work with any version
  107.    //  where the major version number is higher than the original
  108.    //  version you targeted.
  109.  
  110.    if (version < 0x102)
  111.       report_error ("VER");
  112.  
  113.    //  Now check the variant number; for this extension, we only
  114.    //  provide a COMPOSER format.
  115.  
  116.    if (variant != 0) return 0;
  117.  
  118.    (*hDialog) = CreateDialog (hLibInstance, (LPCSTR) "MERGE", hParent, NULL);
  119.    if ((*hDialog) == NULL) return 0;
  120.    return 1;
  121.    }
  122.  
  123.  
  124. void trim_newline (char *str)
  125.    {
  126.    /*  Remove the terminating newline from a string
  127.    **  if it's present. */
  128.  
  129.    int i;
  130.  
  131.    i = strlen (str) - 1;
  132.    while ((i >= 0) && ((str [i] == '\n') || (str [i] == '\r'))) 
  133.       str [i -- ] = '\0';
  134.    }
  135.  
  136.  
  137. static int dump_the_data (FILE *dest, FILE *src, char **fields, int nfields)
  138.    {
  139.    //  Write the formatted data into the temporary file we've created
  140.    //  for the message. Fields are inserted into the message by using
  141.    //  placeholders in the message file: a placeholder consists of a 
  142.    //  tilde character (~), followed by a string of characters, ending
  143.    //  with another tilde. The following placeholders are recognized:
  144.    //
  145.    //  "1" .. "32"  - replaced by the matching field from the data record
  146.    //  "~"          - replaced by a single tilde
  147.  
  148.    int c, state, field;
  149.  
  150.    rewind (src);
  151.    state = 0;
  152.    while ((c = fgetc (src)) != EOF)
  153.       {
  154.       switch (state)
  155.          {
  156.          case 0 :       //  Normal state
  157.             if (c == '~')
  158.                state = 1;
  159.             else
  160.                fputc (c, dest);
  161.             break;
  162.  
  163.          case 1 :       //  Checking a placeholder
  164.             if (c == '~')
  165.                {
  166.                fputc ('~', dest);
  167.                state = 0;
  168.                }
  169.             else
  170.                {
  171.                if (strchr ("01234567890", c) != NULL)
  172.                   {
  173.                   field = c - '0';
  174.                   state = 2;
  175.                   }
  176.                else 
  177.                   {
  178.                   fputc ('~', dest);
  179.                   fputc (c, dest);
  180.                   state = 0;
  181.                   }
  182.                }
  183.             break;
  184.  
  185.          case 2 :
  186.             if (c == '~')
  187.                {
  188.                if ((field > 0) && (field <= nfields))
  189.                   fprintf (dest, "%s", fields [field - 1]);
  190.                state = 0;
  191.                }
  192.             else if (strchr ("01234567890", c) != NULL)
  193.                {
  194.                field *= 10;
  195.                field += (c - '0');
  196.                }
  197.             else 
  198.                {
  199.                fputc ('~', dest);
  200.                fputc (c, dest);
  201.                state = 0;
  202.                }
  203.             break;
  204.          }
  205.       }
  206.  
  207.    return 1;
  208.    }
  209.  
  210.  
  211. static int mailmerge (HWND hWnd)
  212.    {
  213.    //  Perform the actual mail merge:
  214.    //
  215.    //  * Retrieve and validate the parameters in the dialog
  216.    //  * Open the data and format files
  217.    //  * Read the data one line at a time and parse it
  218.    //  * Generate a message per record using the format file.
  219.    //
  220.    //  Returns:  1 on success
  221.    //            0 on failure
  222.  
  223.    char buffer [1024], dfname [MAXFPATH], 
  224.       ffname [MAXFPATH], mfname [MAXFPATH], seps [80];
  225.    char *fields [MAXFIELDS], *s;
  226.    FILE *dfil, *ffil, *mfil;
  227.    HWND hParent = GetParent (hWnd);
  228.    int email_field, i;
  229.  
  230.    //  First, check to see that the user has actually defined
  231.    //  a field separator of some kind.
  232.  
  233.    if (IsDlgButtonChecked (hWnd, IDC_TAB))
  234.       {
  235.       seps [0] = '\t';
  236.       seps [1] = '\0';
  237.       }
  238.    else
  239.       GetDlgItemText (hWnd, IDC_OTHEREDIT, seps, sizeof (seps));
  240.  
  241.    if (seps [0] == '\0')
  242.       {
  243.       report_error ("SEPS");
  244.       return 0;
  245.       }
  246.  
  247.    //  Now get the data filename and the format filename,
  248.    //  verify that they exist, then open them.
  249.  
  250.    GetDlgItemText (hWnd, IDC_DATAFILE, dfname, sizeof (dfname));
  251.    if (access (dfname, 0) != 0)
  252.       {
  253.       report_error ("NSDF");
  254.       return 0;
  255.       }
  256.  
  257.    GetDlgItemText (hWnd, IDC_FORMATFILE, ffname, sizeof (ffname));
  258.    if (access (ffname, 0) != 0)
  259.       {
  260.       report_error ("NSFF");
  261.       return 0;
  262.       }
  263.  
  264.    if ((dfil = fopen (dfname, "rt")) == NULL)
  265.       {
  266.       report_error ("NSDF");
  267.       return 0;
  268.       }
  269.  
  270.    if ((ffil = fopen (ffname, "rt")) == NULL)
  271.       {
  272.       fclose (dfil);
  273.       report_error ("NSFF");
  274.       return 0;
  275.       }
  276.  
  277.    //  Now tell the Extension Manager to create a message for us...
  278.  
  279.    if (SendMessage (hParent, WM_F_NEWMESSAGE, 0, 0) == 0)
  280.       {
  281.       report_error ("MSGF");
  282.       return 0;
  283.       }
  284.  
  285.    // ... and fill out the basic fields in the message.
  286.    
  287.    GetDlgItemText (hWnd, IDC_SUBJECT, buffer, sizeof (buffer));
  288.  
  289.    SendMessage (hParent, WM_F_SUBJECT, 0, (LPARAM) buffer);
  290.    if (IsDlgButtonChecked (hWnd, IDC_COPYSELF))
  291.       SendMessage (hParent, WM_F_COPYSELF, 1, 0);
  292.    if (IsDlgButtonChecked (hWnd, IDC_URGENT))
  293.       SendMessage (hParent, WM_F_URGENT, 1, 0);
  294.    if (IsDlgButtonChecked (hWnd, IDC_CONFIRMREADING))
  295.       SendMessage (hParent, WM_F_CONFIRMREADING, 1, 0);
  296.    if (IsDlgButtonChecked (hWnd, IDC_USEMIME))
  297.       SendMessage (hParent, WM_F_MIME, 1, 0);
  298.  
  299.    GetDlgItemText (hWnd, IDC_EMAIL, buffer, sizeof (buffer));
  300.    email_field = atoi (buffer);
  301.    if (email_field < 1) email_field = 1;
  302.    -- email_field;
  303.  
  304.    //  Now we're down to business: we parse the data file one
  305.    //  line at a time, creating a temporary file for each message
  306.    //  we create, then send it.
  307.  
  308.    while (fgets (buffer, sizeof (buffer), dfil) != NULL)
  309.       {
  310.       trim_newline (buffer);
  311.       if (buffer [0] == '\0') continue;      //  Blank line
  312.       i = 1;
  313.       s = buffer;
  314.       fields [0] = buffer;
  315.       while (*s)
  316.          {
  317.          if (strchr (seps, *s) != NULL)
  318.             {
  319.             *s = '\0';
  320.             if (i < MAXFIELDS)
  321.                fields [i ++] = s + 1;
  322.             }
  323.          ++ s;
  324.          }
  325.  
  326.       fields [i] = NULL;
  327.  
  328.       SendMessage (hParent, WM_F_TEMPFILE, sizeof (mfname), (LPARAM) mfname);
  329.       if ((mfil = fopen (mfname, "wt")) == NULL)
  330.          {
  331.          fclose (dfil);
  332.          fclose (ffil);
  333.          report_error ("TMPF");
  334.          return 0;
  335.          }
  336.  
  337.       dump_the_data (mfil, ffil, fields, i);
  338.       fclose (mfil);
  339.       SendMessage (hParent, WM_F_TO, 0, (LPARAM) (fields [email_field]));
  340.       SendMessage (hParent, WM_F_MESSAGEFILE, 0, (LPARAM) mfname);
  341.       SendMessage (hParent, WM_F_SENDMESSAGE, 0, 0);
  342.       }
  343.  
  344.    fclose (dfil);
  345.    fclose (ffil);
  346.    return 1;
  347.    }
  348.  
  349.  
  350. #pragma warn -use
  351.  
  352. LONG FAR PASCAL _export MrgProc (HWND hWnd, UINT wMsg, 
  353.    WPARAM wParam, LPARAM lParam)
  354.    {
  355.    //  Service routine for the form's enclosed dialog. This is a
  356.    //  standard windows modeless WndProc.
  357.  
  358.    DWORD dwResult = 0;
  359.    BOOL fCallDefProc = TRUE;
  360.    char buffer [256], *s, *to, *subj;
  361.    RECT r;
  362.  
  363.    switch (wMsg)
  364.       {
  365.       case WM_FM_INIT :
  366.          EnableWindow (GetDlgItem (hWnd, IDC_OTHEREDIT), FALSE);
  367.          CheckRadioButton (hWnd, IDC_TAB, IDC_OTHER, IDC_TAB);
  368.  
  369.          //  Now construct our help file name
  370.          GetModuleFileName (hLibInstance, buffer, sizeof (buffer));
  371.          s = strrchr (buffer, '.');
  372.          if (s != NULL)
  373.             {
  374.             strcpy (s, ".HLP");
  375.             strcpy (helpname, buffer);
  376.             }
  377.          break;
  378.  
  379.       case WM_DESTROY :
  380.          //  A good place to store some preferences, perhaps.
  381.          if (help_used) WinHelp (hWnd, helpname, HELP_QUIT, 0);
  382.          break;
  383.  
  384.       case WM_FM_INITFOCUS :
  385.          SetFocus (GetDlgItem (hWnd, 101));
  386.          break;
  387.  
  388.       case WM_FM_RESTOREFOCUS :
  389.          //  There's a glitch in the way the Windows MDI manager
  390.          //  seems to manage focus which means that we can have
  391.          //  focus taken away and not returned when the user does
  392.          //  anything outside our window. WinPMail has some quite
  393.          //  complex logic to deal with this case and will send
  394.          //  this message whenever focus should be restored in
  395.          //  our window. We set focus to the last active control
  396.          //  (which we know from trapping EN_SETFOCUS messages).
  397.  
  398.          if (last_focus) SetFocus (last_focus);
  399.          break;
  400.  
  401.       case WM_COMMAND :
  402.          fCallDefProc = FALSE;
  403.  
  404. #ifdef NOPORTLAYER
  405.          if (HIWORD(lParam) == EN_SETFOCUS)
  406. #else
  407.          if (GET_WM_COMMAND_CMD(wParam, lParam) == EN_SETFOCUS)
  408. #endif
  409.             {
  410.             //  We have to trap EN_SETFOCUS messages so we know which
  411.             //  control was active last. When a menu selection is made
  412.             //  our current control will lose focus and because of a
  413.             //  curiosity in Windows' MDI management, we won't get it
  414.             //  back. Instead, WinPMail generates a WM_FM_RESTOREFOCUS
  415.             //  message which signals to us that we should set focus to
  416.             //  whatever the last active control was.
  417.  
  418. #ifdef NOPORTLAYER
  419.             last_focus = (HWND) LOWORD (lParam);
  420. #else
  421.             last_focus = GET_WM_COMMAND_HWND(wParam, lParam);
  422. #endif
  423.             break;
  424.             }
  425.  
  426. #ifdef NOPORTLAYER
  427.          switch (wParam)
  428. #else
  429.          switch (GET_WM_COMMAND_ID(wParam, lParam))
  430. #endif
  431.             {
  432.             case IDC_TAB :
  433.                EnableWindow (GetDlgItem (hWnd, IDC_OTHEREDIT), FALSE);
  434.                CheckRadioButton (hWnd, IDC_TAB, IDC_OTHER, IDC_TAB);
  435.                break;
  436.  
  437.             case IDC_OTHER :
  438.                EnableWindow (GetDlgItem (hWnd, IDC_OTHEREDIT), TRUE);
  439.                CheckRadioButton (hWnd, IDC_TAB, IDC_OTHER, IDC_OTHER);
  440.                break;
  441.  
  442.             case IDC_BROWSEDATAFILE :
  443.                if (SendMessage (GetParent (hWnd), WM_F_BROWSEFILE, 1, (LPARAM) buffer))
  444.                   {
  445.                   SendDlgItemMessage (hWnd, IDC_DATAFILE, EM_SETSEL, 
  446.                      0, (LPARAM) 0x7FFF0000L);
  447.                   SendDlgItemMessage (hWnd, IDC_DATAFILE, EM_REPLACESEL,
  448.                      0, (LPARAM) buffer);
  449.                   }
  450.                break;
  451.  
  452.             case IDC_BROWSEFORMATFILE :
  453.                if (SendMessage (GetParent (hWnd), WM_F_BROWSEFILE, 1, (LPARAM) buffer))
  454.                   {
  455.                   SendDlgItemMessage (hWnd, IDC_FORMATFILE, EM_SETSEL, 
  456.                      0, (LPARAM) 0x7FFF0000L);
  457.                   SendDlgItemMessage (hWnd, IDC_FORMATFILE, EM_REPLACESEL,
  458.                      0, (LPARAM) buffer);
  459.                   }
  460.                break;
  461.  
  462.             case IDC_HELP :
  463.                if (helpname [0])
  464.                   {
  465.                   help_used = 1;
  466.                   WinHelp (hWnd, helpname, HELP_CONTEXT, 1);
  467.                   }
  468.                else
  469.                   MessageBeep (0);
  470.                break;
  471.  
  472.             case IDC_SEND :
  473.                if (! mailmerge (hWnd)) break;
  474.                //  Drop through and close up shop...
  475.  
  476.             case IDC_CANCEL :
  477.                PostMessage (GetParent (hWnd), WM_CLOSE, 0, 0);
  478.                break;
  479.             }
  480.          break;
  481.       }
  482.  
  483.    if (fCallDefProc)
  484.       dwResult = BWCCDefDlgProc (hWnd, wMsg, wParam, lParam);
  485.  
  486.    return dwResult;
  487.    }
  488.  
  489.  
  490. #pragma warn -sus
  491.  
  492. void unregister_form_classes (void)
  493.    {
  494.    //  Remove any classes associated with the form; we have the
  495.    //  same problem here as we do with registering the classes
  496.    //  for the DLL - we only want to deregister the classes on
  497.    //  the last time we're unloaded.
  498.  
  499.    if (GetModuleUsage (hLibInstance) > 1) return;      //  Not a problem
  500.    UnregisterClass (szFormDlgClassName, hLibInstance);
  501.    }
  502.  
  503.  
  504. BOOL FAR PASCAL LibMain (HINSTANCE hInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
  505.    {
  506.    WNDCLASS wc;
  507.  
  508.    if (! hLibInstance)
  509.       {
  510.       hLibInstance = hInst;
  511.       BWCCGetVersion ();      //  Forces BWCC to be dynamically loaded.
  512.  
  513.       //  Register any window classes used by the form. Forms will usually
  514.       //  register either one or occasionally two classes which define
  515.       //  the composition and reader dialogs created by the DLL.
  516.       //
  517.       //  There's a gotcha here, of course (aren't there always, in
  518.       //  Windows?)... You can't register a window class more than once,
  519.       //  so if the DLL has already been loaded and the user asks to
  520.       //  create a second instance of the form, we have to be careful
  521.       //  not to re-register the class. We do this by checking the
  522.       //  instance usage counter of the DLL - if it's greater than 1,
  523.       //  then we DON'T register the classes.
  524.  
  525. #pragma warn -sig
  526.       wc.style          = WS_CHILD;
  527. #pragma warn .sig
  528.       wc.lpfnWndProc    = MrgProc;
  529.       wc.cbClsExtra     = 0;
  530.       wc.cbWndExtra     = DLGWINDOWEXTRA;
  531.        wc.hInstance      = hLibInstance;
  532.        wc.hIcon          = NULL;
  533.       wc.hCursor        = LoadCursor (NULL, IDC_ARROW);
  534.       wc.hbrBackground  = (HBRUSH) (COLOR_WINDOW + 1);
  535.       wc.lpszMenuName   = NULL;
  536.       wc.lpszClassName  = szFormDlgClassName;
  537.       if (! RegisterClass (&wc))
  538.          MessageBeep (0);
  539.       }
  540.  
  541.    return (TRUE);             // Initialization went OK
  542.    }
  543.  
  544.